package com.self.ry.utils;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import java.security.Security;
import java.util.Arrays;

/**
 * Copyright: Copyright (c) 2019
 *
 * @Description: RIPEMD-160是基于Merkle-Damgård构造的加密散列函数。
 * <p>
 * 在比特币标准中使用。它是ripemd算法的增强版本，生成128位哈希摘要，而ripemd-160算法生成160位输出。
 * 压缩功能由80个阶段组成，由5个模块组成，每个模块运行16次。此模式运行两次，结果在底部使用模32加法组合。
 *
 * 压缩函数处理16个32bit的无符号字。这要求将消息填充到512bit的倍数，并将字节流输入填充为32bit字。
 * 填充方案与MD4相同，使用Merkle–Damgård加强以防止长度扩展攻击。
 * 这包括添加到消息末尾的消息和添加到块末尾的消息长度（以位为单位）。字节首先被推入低端这个词。下面是4个将消息填充到单词中的示例，以显示不同消息长度的可能模式：
 * @Version: v1.0.0
 * @Author ruyi
 * @Date 2019/6/21 9:42
 */
public final class RipeMD160Util {

    /**
     * RipeMD160消息摘要
     *
     * @param data         seed ECU下发的随机种子数
     * @param symmetricKey 给定的对称密钥
     * @return byte[] 消息摘要
     */
    public static byte[] encodeRipeMD160(byte[] data, byte[] symmetricKey) throws Exception {
        //加入BouncyCastleProvider的支持
        Security.addProvider(new BouncyCastleProvider());

        RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest();
        int length = data.length;
        byte[] bytes = Arrays.copyOf(symmetricKey, length + symmetricKey.length);
        System.arraycopy(data, 0, bytes, symmetricKey.length, length);

        // 使用一个字节块更新消息摘要。
        ripemd160Digest.update(bytes, 0, bytes.length);
        // 长度20
        byte[] out = new byte[ripemd160Digest.getDigestSize()];
        // 关闭摘要，产生最终的摘要值。 doFinal调用使摘要重置。
        ripemd160Digest.doFinal(out, 0);
        byte[] keys = new byte[length];
        System.arraycopy(out, 0, keys, 0, length);
        symmetricKey = null;
        data = null;
        bytes = null;
        out = null;
        return keys;
    }

    /**
     * RipeMD160Hex消息摘要
     *
     * @param data         seed ECU下发的随机种子数
     * @param symmetricKey 给定的对称密钥
     * @return String 消息摘要
     **/
    public static String encodeRipeMD160Hex(byte[] data, byte[] symmetricKey) throws Exception {
        //执行消息摘要
        byte[] b = encodeRipeMD160(data, symmetricKey);
        System.out.println("encodeRipeMD160Hex=" + ByteHexUtil.Bytes2String(b, b.length));
        //做十六进制的编码处理
        return new String(Hex.encode(b));
    }


    /**
     * dll文件如果是32位的，则需要32位的JDK环境
     * 如果是64位的，则需要64位的JDK环境
     *
     * 此DLL是32位操作
     */
    interface RipeMD160 extends Library {
        RipeMD160 INSTANCE = (RipeMD160) Native.load((Platform.isWindows() ? "dll\\DummySeedS17" : "c"), RipeMD160.class);

        void seed2key(String seed, byte[] key);
    }


    /**
     * 将seed字节种子转成十进制的字符串
     *
     * @param seed
     * @return
     */
    public static String seedByte2String(byte[] seed) {
        if (seed == null || seed.length == 0) {
            return "";
        }

        StringBuilder sb = new StringBuilder("0");
        for (int m = 0, mLen = seed.length; m < mLen; m++) {
            sb.append(" ");
            sb.append(String.valueOf(seed[m] & 0xFF));
        }

        return sb.toString();
    }

    /**
     * seed字节数是根据DLL文件来的
     * 现在的DLL对应的是8字节
     *
     * @param seed
     */
    public static byte[] seed2Key(byte[] seed) {
        int len = seed.length;
        String strSeed = seedByte2String(seed);
        // 0x96 0x70 0x1E 0xD4 0x63 0x60 0x0A 0x82
        //String seed = "0 150 112 30 212 99 96 10 130";
        byte[] key = new byte[len];
        RipeMD160.INSTANCE.seed2key(strSeed, key);

        return key;
    }

    public static String seed2KeyStr(byte[] seed) {
        byte[] key = seed2Key(seed);
        return ByteHexUtil.Bytes2String(key, key.length);
    }

    public static void main(String[] args) throws Exception {
        // RipeMD160 start
        String privateKey = "D2 53 87 49 91 C5 3B B3 A6 1F 19 5A 7A AB D6 02";
        String seed = "FD 28 E4 92 6A F6 33 77";
        seed = seed.replaceAll("\\s+", "");
        byte[] seeds = ByteHexUtil.String2Bytes(seed);
        privateKey = privateKey.replaceAll("\\s+", "");
        byte[] bytes = ByteHexUtil.String2Bytes(privateKey);
        // 00 87 7D E6 65 59 3E 70
        encodeRipeMD160Hex(seeds, bytes);
        // RipeMD160 end

        // 7065BA735E8F4BB5
        /*String strSeed = "96 70 1E D4 63 60 0A 82";
        //String strSeed = "2C 8A 6B CA 2F 15 85 21";
        strSeed = strSeed.replaceAll("\\s+", "");
        byte[] seed = ByteHexUtil.String2Bytes(strSeed);
        String s = seed2KeyStr(seed);
        System.out.println(s);*/
    }
}
